import Base from '../bagua/base.js'
import Mountain from '../bagua/mountain.js'
import Zhi from '../bagua/zhi.js'
import Gan from '../bagua/gan.js'
import Ganzhi from '../bagua/ganzhi.js'
import God12 from '../bagua/god12.js'
import Younian from '../bagua/younian.js'

import Dunjia from './dunjia.js'

import dictionary from '../bagua/dictionary.js'

// 山向局的类型
const typeList = ['year', 'month', 'day', 'dragon']
// 山向局的变式
const transList = ['正盘', '归一', '合十', '反转']

class PosDunjia extends Dunjia {
  /*
    注意options.angle必须是字符串
  */
  constructor(options = {}) {
    super(options)
    
    this.types.push('PosDunjia')
  }
  
  // 重写，还要初始化二十四山向
  initStruct () {
    // 初始化九宫八卦
    this.palaces = this.initPalaces()
    // 初始化地支十二神
    this.zhiPalaces = this.initZhiPalaces()
    // 初始化二十四山向
    this.mountainPalaces = this.initMountainPalaces()
    
    // 最后联立各宫
    this.connect()
  }
  
  // 先起山向
  initMountain () {
    // 记住角度
    this.angle = parseFloat(this.options.angle)
    
    // 根据角度定向
    this.direction = new Mountain(this.options.angle, {
      description: dictionary.DIRECTION,
      allDetail: true
    })
    // 向的对面就是山
    const oppositeAngle = Mountain.getOppositeAngle(this.direction.angle)
    this.mountain = new Mountain(oppositeAngle.toString(), {
      description: dictionary.MOUNTAIN,
      allDetail: true
    })
  }
  
  // 重写起干支的方法
  initGanzhi () {
    this.initMountain()
    
    this.keyGanzhiGroup = []
    
    let index = typeList.indexOf(this.options.posDunjiaType)
    // 默认为年局
    if (index < 0) { index = 0 }
    if (index === 3) {
      // 地龙局，用地龙作为第一柱
      this.keyGanzhiGroup.push(this.mountain.groundDragon)
    }
    else {
      this.keyGanzhiGroup.push(this.date.ganzhi[index])
    }
    
    // 用五鼠遁计算第二柱
    // 以向定时
    const zhiName = Mountain.getZhiMountain(this.direction.index)
    const zhiIndex = Zhi.zhiList.indexOf(zhiName)
    const ganIndex = this.keyGanzhiGroup[0].gan.getMouseGanIndex(zhiIndex)
    this.keyGanzhi = new Ganzhi(ganIndex, zhiIndex, null, {
      description: '定局' + dictionary.TIMEKEY
    })
    this.keyGanzhiGroup.push(this.keyGanzhi)
    
    // 取第一柱地支的合支作为将神
    const heIndex = this.keyGanzhiGroup[0].zhi.getHeIndex()
    const heDescription = dictionary.JIANG_GOD + '('
      + (index >= 3 ? '龙' : dictionary.TIME[index])
      + ')'
    this.jiangGod = new Zhi(heIndex, {
      description: heDescription
    })
  }
  
  // 重写定阴阳的方法
  initYinyang () {
    // 判断山向局的变式
    let transIndex = transList.indexOf(this.options.posDunjiaTrans)
    transIndex = transIndex < 0 ? 0 : transIndex
    this.posDunjiaTrans = transList[transIndex]
    
    // 以山定局
    this.isSolar = this.mountain.numData.isSolar
    
    // 特殊变式处理
    if (transIndex > 0) {
      this.isSolar = !this.isSolar
    }
  }
  
  // 重写定局数的方法
  initNum () {
    // 以山定局
    this.num = this.mountain.numData.num
    
    // 特殊变式处理
    if (this.posDunjiaTrans === transList[1]) {
      // 归一，局数相加等于9，阴阳颠倒
      this.num = 9 - this.num <= 0 ? 9 : 9 - this.num
    }
    else if (this.posDunjiaTrans === transList[2]) {
      // 合十，局数相加等于10，阴阳颠倒
      this.num = 10 - this.num
    }
  }
  
  /* @section 获取外圈神煞 */
  // 排十二建神，山向重写该方法
  setJianchu (god, extra) {
    const eleKey = Dunjia.getOuterKey(god, extra)
    if (!this.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      // 确认目标是山或向
      let des = !this[extra] ? this.direction : this[extra]
      // 用双山五行化成地支
      const zhiName = Mountain.getZhiMountain(des.index)
      const zhiIndex = Zhi.zhiList.indexOf(zhiName)
      const len = Zhi.zhiList.length
      this.traveZhiByClock(zhiIndex, len, (item, index, list) => {
        item.ele[eleKey] = new God12(index, 'jianchu', {
          description: item.description + dictionary.JIANCHU
        })
        return item
      })
    }
    // 返回引导信息
    return {
      key: eleKey,
      divide: 12,
      extraInfo: []
    }
  }
  
  // 排十二长生，山向重写该方法
  setTwelveState (god, extra) {
    const eleKey = Dunjia.getOuterKey(god, extra)
    if (!this.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      // 确认目标是山或向
      let des = !this[extra] ? this.direction : this[extra]
      // 用双山五行化成地支
      const zhiName = Mountain.getZhiMountain(des.index)
      const zhi = new Zhi(zhiName)
      // 获取长生位
      const startIndex = zhi.bornZhi
      const len = Zhi.zhiList.length
      
      this.traveZhiByClock(startIndex, len, (item, index, list) => {
        item.ele[eleKey] = new God12(index, 'twelveState', {
          description: item.description + dictionary.TWELVE_STATE
        })
        return item
      })
    }
    return {
      key: eleKey,
      divide: 12,
      extraInfo: []
    }
  }
  
  // 排山向零散的其他神煞，山向重写该方法
  setOtherPosGod (god, extra) {
    const eleKey = Dunjia.getOuterKey(god, extra)
    
    // 需要计算的值
    let desIndexes = null
    let godName = ''
    let extraInfoBagua = []
    let result = {}

    if (!this.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      const typeList = extra.split(' ')
      // 确认目标是山或向，第二个参数决定
      const mountainType = typeList[1] ? typeList[1] : 'direction'
      let des = !this[mountainType] ? this.direction : this[mountainType]
      // 用双山五行获取天干
      const ganName = des.getGanMountain()
      const gan = new Gan(ganName)
      const len = Zhi.zhiList.length
      // 确定神煞类型，默认为桃花
      const godType = typeList[0] ? typeList[0] : 'taohua'

      // 开始计算神煞
      if (godType === 'taohua') {
        // 桃花在败地
        desIndexes = [gan.getZhiByTwelveState(1)]
        godName = '桃花'
      }
      else if (godType === 'wenchang') {
        // 文昌是正印，取地支
        desIndexes = gan.getGanzhiByTenGod(6, false)
        godName = '文昌'
      }
      
      if (desIndexes !== null) {
        this.traveZhiByClock(0, len, (item, index, list) => {
          if (desIndexes.indexOf(index) >= 0) {
            item.ele[eleKey] = new Base({
              name: godName,
              description: item.description + godName
            })
            item.ele[eleKey].isAlert = true
            extraInfoBagua.push(item.ele.zhi)
          }
          else {
            item.ele[eleKey] = null
          }
          
          return item
        })
        // 获取返回结果
        result = {
          key: eleKey,
          divide: 12,
          extraInfo: [{
            text: godName,
            bagua: extraInfoBagua
          }]
        }       
      }
      else {
        result = {
          key: eleKey,
          divide: 12,
          extraInfo: []
        }   
      }
      
      // 缓存结果
      if (!this.eleCache) { this.eleCache = {} }
      this.eleCache[eleKey] = result
    }
    else {
      // 若已经具备，则直接获取返回值
      result = this.eleCache[eleKey]
    }
    
    return result
  }
  
  // 排山向的将神，山向重写该方法
  setJiangGod (god, extra) {
    const eleKey = Dunjia.getOuterKey(god, extra)
    
    if (!this.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      // 将神加在定局支上，顺排一圈
      let curZhi = this.jiangGod.index
      const startIndex = this.keyGanzhi.zhi.index
      const len = Zhi.zhiList.length
      // 山上找黄泉煞
      const huangquanIndex = this.mountain.huangquan.index
      
      this.traveZhiByClock(startIndex, len, (item, index, list) => {
        item.ele[eleKey] = new Zhi(curZhi, {
          description: item.description + dictionary.SKY_ZHI,
          yuejiangKey: true
        })
        if (huangquanIndex === item.ele[eleKey].index) {
          // 黄泉煞加粗
          item.ele[eleKey].isBold = true
        }
        
        curZhi = (curZhi + 1) % len
        return item
      })
    }
    
    let typeIndex = typeList.indexOf(this.options.posDunjiaType)
    const text = typeIndex >= 3 ? '将神(龙)' : dictionary.TIME[typeIndex] + '将' 
    return {
      key: eleKey,
      divide: 12,
      extraInfo: [
        {
          text: text,
          bagua: this.jiangGod
        },
        {
          text: '黄泉煞',
          bagua: this.mountain.huangquan
        }
      ]
    }
  }
  
  // 排山向的关联八卦，山向重写该方法
  setRelativeBagua (god, extra) {
    const eleKey = Dunjia.getOuterKey(god, extra)
    
    if (!this.mountainPalaces[0].ele.hasOwnProperty(eleKey)) {
      const len = Mountain.mountainList.length
      this.traveMountainByClock(0, len, (item, index) => {
        item.ele[eleKey] = item.ele.mountain.najiaBagua
        return item
      })
    }
    
    return {
      key: eleKey,
      divide: 24,
      extraInfo: []
    }
  }
  
  // 小游年，山向用，虚函数
  setSmallYounian (god, extra) {
    const eleKey = Dunjia.getOuterKey(god, extra)
    
    // 确认目标是山或向
    let des = !this[extra] ? this.direction : this[extra]
    if (!this.mountainPalaces[0].ele.hasOwnProperty(eleKey)) {
      const len = Mountain.mountainList.length
      // 参照八卦确认
      const desBagua = des.najiaBagua
      
      this.traveMountainByClock(0, len, (item, index) => {
        item.ele[eleKey] = new Younian([item.ele.mountain.najiaBagua, desBagua, true], {
          description: item.description + dictionary.SMALL_YOUNIAN + (extra === 'mountain' ? '(山伏位)' : '(向伏位)')
        })
        return item
      })
    }
    
    return {
      key: eleKey,
      divide: 24,
      isSim: true,
      extraInfo: [{
        text: '伏位',
        bagua: des
      }]
    }
  }
  
  /* @section 获取信息 get */
  // 获取山向信息文字
  getMountainText () {
    let result = `${this.mountain.name}山${this.direction.name}向`
    if (this.options.posDunjiaType === 'dragon') {
      // 角度范围
      result = result + '  ' + `${this.direction.dragonSection.start.toFixed(2)}° ~ ${this.direction.dragonSection.end.toFixed(2)}°`
      // 计算透地龙信息
      result = result + '  ' + this.mountain.groundDragon.name + '(龙)'
    }
    else {
      result = result + '  ' + `${this.direction.numData.range.start.toFixed(2)}° ~ ${this.direction.numData.range.end.toFixed(2)}°`
    }
    
    // 是正盘还是归一、合十、反转
    result = result + (this.posDunjiaTrans === transList[0] ? '' : `  ${this.posDunjiaTrans}`)
    
    return result
  }
  
  /* @section static 静态方法 */
  // 获取可以选的所有局
  static getNeighborSections (angle, type = 'year') {
    let result = []
    const palaceAngle = Mountain.getPalaceAngleRange(angle)
    if (type !== 'dragon') {
      // 一般山向获取可选局
      let curAngle = palaceAngle.start
      const delta = Mountain.angleSub(Math.ceil(palaceAngle.end), palaceAngle.start)
      const len = Math.floor(delta / Mountain.NUM_RANGE)
      for (let i = 0; i < len; i++) {
        const index = Math.floor(curAngle / Mountain.MOUNTAIN_RANGE)
        let section = {
          positionIndex: index,
          mountainIndex: Mountain.getOppositeMountain(index),
          start: curAngle,
          end: Mountain.standardAngle(curAngle + Mountain.NUM_RANGE - Mountain.RANGE_TAIL)
        }
        section.text = `${Mountain.mountainList[section.mountainIndex]}山${Mountain.mountainList[section.positionIndex]}向`
        section.text = section.text + '  ' + `${section.start.toFixed(2)}° ~ ${section.end.toFixed(2)}°`
        result.push(section)
        
        curAngle = (curAngle + Mountain.NUM_RANGE) % Mountain.MAX_ANGLE
      }
    }
    else {
      // 地龙山向获取可选局
      if (!Mountain.dragonSections) {
        Mountain.initDragonSections()
      }
      
      const sections = Mountain.dragonSections
      const len = sections.length
      const startIndex = sections.findIndex(item => item === palaceAngle.start)
      const endIndex = sections.findIndex(item => item === Math.ceil(palaceAngle.end))
      const delta = endIndex >= startIndex ? endIndex - startIndex : (endIndex + len - startIndex)
      for (let i = 0; i < delta; i++) {
        const index = (startIndex + i) % len
        const section = {
          start: sections[index],
          end: Mountain.standardAngle(sections[(index + 1) % len] - Mountain.RANGE_TAIL)
        }
        // 计算山向
        const detail = Mountain.getMountainDetailFromAngle(section.start)
        section.positionIndex = detail.index,
        section.mountainIndex = Mountain.getOppositeMountain(detail.index)
        // 计算山的地龙
        const oppositeAngle = Mountain.getOppositeAngle(section.start)
        const groundMountain = Mountain.getMountainDetailFromAngle(oppositeAngle, Mountain.mountainTypes[1])
        section.groundDragon = Mountain.getGroundDragon(oppositeAngle, groundMountain)
        // 计算山的上中下三元
        const humanMountain = Mountain.getMountainDetailFromAngle(oppositeAngle)
        const numData = Mountain.getNumData(oppositeAngle, humanMountain)
        section.isSolar = numData.isSolar
        section.num = numData.num
        // 描述文字
        section.text = `${Mountain.mountainList[section.mountainIndex]}山${Mountain.mountainList[section.positionIndex]}向`
        section.text = section.text +  ' ' + `${section.start.toFixed(2)}° ~ ${section.end.toFixed(2)}°`
        section.text = section.text +  ' ' + `${section.groundDragon}`
        section.text = section.text +  ' ' + (section.isSolar ? '阳' : '阴') + section.num + '局'
        result.push(section)
      }
    }
    return result
  }
}

Object.assign(PosDunjia, {})

export default PosDunjia